import 'dart:async';
import 'dart:io';
import 'oauth_interceptor.dart';
import 'extra_keys.dart';
import 'error_code.dart';
import 'toast_interceptor.dart';
import 'package:dio/dio.dart';
import 'response_interceptor.dart';
import 'response_data.dart';
import '../config/configs.dart';
import 'error_interceptor.dart';
import 'http_exception.dart' as prefix0;

class HttpClient {
  static final HttpClient client = HttpClient._init();

  /// 请求超时时间
  final int _connectTimeout = Configs.NETWORK_CONNECT_TIMEOUT;

  /// 响应超时时间
  final int _receiveTimeout = Configs.NETWORK_RECEIVE_TIMEOUT;

  /// api请求地址
  final String _baseUrl = Configs.NETWORK_BASE_API_URL;

  /// 是否开启调试
  final bool _isDebug = Configs.DEBUG;

  /// 是否打印请求日志
  final bool _isLogsEnabled = Configs.LOGS_ENABLED;

  /// 用于区分请求所属客户端的参数
  final String _clientId = Configs.CLIENT_ID;

  /// 请求的Content-Type，默认值是"application/json; charset=utf-8".
  /// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
  /// 统一全部默认为,不作更改 `Headers.formUrlEncodedContentType`
  // static final String form = Headers.formUrlEncodedContentType;
  static final String form = Headers.jsonContentType;

  late Dio _dio;
  late Dio _tokenDio;

  /// Options.
  BaseOptions? _options;

  /// 设置默认的options
  BaseOptions getDefOptions() {
    BaseOptions options = BaseOptions(
      baseUrl: _baseUrl,
      connectTimeout: Duration(milliseconds: _connectTimeout),
      receiveTimeout: Duration(milliseconds: _receiveTimeout),
      contentType: form,
      // responseType: ResponseType.json,
      headers: {
        HttpHeaders.userAgentHeader: "dio",
        "api-version": "1.0.0",
        "plateform": Platform.operatingSystem,
        "requestSource": _clientId,
      },
      validateStatus: (status) => status! >= 200 && status < 400,
      extra: {
        ExtraKeys.NEED_ERROR_TOAST: true,
        ExtraKeys.NEED_TOAST: false,
        ExtraKeys.NEED_TOKEN: true,
        ExtraKeys.NEED_LOADING: false,
      },
    );
    return options;
  }

  HttpClient._init() {
    _options = getDefOptions();
    _dio = Dio(_options);
    _tokenDio = Dio(_options);

    // flutter使用dio请求时(默认抓取不了)
    // 使用Charles对Flutter应用程序抓包
    // 设置代理, 可以在这里进行mock
    // _dio.httpClientAdapter = DefaultHttpClientAdapter();
    // (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
    //     (client) {
    //   client.findProxy = (uri) {
    //     //proxy all request to localhost:8888
    //     return "PROXY 192.168.0.5:8888";
    //   };
    //   client.badCertificateCallback =
    //       (X509Certificate cert, String host, int port) => true;
    // };

    // oauth2 拦截器放在第1位
    OauthInterceptor oauthInterceptor = OauthInterceptor();
    _dio.interceptors.add(oauthInterceptor);

    //// 请求缓存拦截器第2位
    //DioCacheManager cacheManager = DioCacheManager(CacheConfig(
    //  baseUrl: _baseUrl,
    //  defaultMaxAge: Duration(days: 30),
    //  defaultMaxStale: Duration(days: 7),
    //));
    //// cacheManager.clearAll();
    //_dio.interceptors.add(cacheManager.interceptor);

    // 响应解析拦截器放在第3位
    ResponseInterceptor responseInterceptor = ResponseInterceptor();
    _dio.interceptors.add(responseInterceptor);
    _tokenDio.interceptors.add(responseInterceptor);

    // 添加错误处理拦截器放在第4位
    ErrorInterceptor errorInterceptor = ErrorInterceptor();
    _dio.interceptors.add(errorInterceptor);
    _tokenDio.interceptors.add(errorInterceptor);

    // 网络弹出提示拦截器放在第5位
    ToastInterceptor toastInterceptor = ToastInterceptor();
    _dio.interceptors.add(toastInterceptor);
    _tokenDio.interceptors.add(toastInterceptor);

    // 添加日志打印拦截器, 放在最后面
    if (_isLogsEnabled) {
      LogInterceptor logInterceptor = LogInterceptor(
        requestBody: true,
        responseBody: true,
      );
      _dio.interceptors.add(logInterceptor);
      _tokenDio.interceptors.add(logInterceptor);
    }
  }

  /// 可使用 RequestOptions(connectTimeout: connectTimeout,)方法修改参数
  Future<ResponseData<T>> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
  }) async {
    return await (_dio
        .get<ResponseData<T>>(
          path,
          queryParameters: queryParameters,
          options: _mergeOption(
            options,
            // buildCacheOptions(
            //   Duration(days: 0),
            //   maxStale: Duration(days: 7),
            //   options: options,
            //   forceRefresh: true,
            // ),
            form,
          ),
          cancelToken: cancelToken,
          onReceiveProgress: onReceiveProgress,
        )
        .then((r) => _resolve(r.data))
        .catchError((e) => _reject(e)) as FutureOr<ResponseData<T>>);
  }

  /// 默认post, queryParameters是url参数.data是body参数,json格式
  Future<ResponseData<T>> post<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    return await (_dio
        .post<ResponseData<T>>(
          path,
          data: data,
          options: options,
          queryParameters: queryParameters,
          cancelToken: cancelToken,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress,
        )
        .then((r) => _resolve(r.data))
        .catchError((e) => _reject(e)) as FutureOr<ResponseData<T>>);
  }

  /// 表单提交, queryParameters是url参数.data是表单键值对参数
  Future<ResponseData<T>> formPost<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) {
    return this.post<T>(
      path,
      data: data,
      options: options,
      queryParameters: queryParameters,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
  }

  /// 带文件表单提交, queryParameters是url参数.data是表单键值对参数(文件也是键值对)
  Future<ResponseData<T>> formDataPost<T>(
    String path, {
    required data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) {
    return this.post<T>(
      path,
      data: FormData.fromMap(data),
      options: options,
      queryParameters: queryParameters,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
  }

  Future<ResponseData<dynamic>> tokenPost<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    return await _tokenDio
        .post<ResponseData<T>>(
          path,
          data: data,
          options: options,
          queryParameters: queryParameters,
          cancelToken: cancelToken,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress,
        )
        .then((r) => _resolve(r.data))
        .catchError((e) => _reject(e));
  }

  Future<ResponseData<dynamic>> put<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    return await _dio
        .put<ResponseData<T>>(
          path,
          data: data,
          queryParameters: queryParameters,
          options: options,
          cancelToken: cancelToken,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress,
        )
        .then((r) => _resolve(r.data))
        .catchError((e) => _reject(e));
  }

  Future<ResponseData<dynamic>> head<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    return await _dio
        .head<ResponseData<T>>(
          path,
          data: data,
          queryParameters: queryParameters,
          options: options,
          cancelToken: cancelToken,
        )
        .then((r) => _resolve(r.data))
        .catchError((e) => _reject(e));
  }

  Future<ResponseData<dynamic>> delete<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    return await _dio
        .delete<ResponseData<T>>(
          path,
          data: data,
          queryParameters: queryParameters,
          options: options,
          cancelToken: cancelToken,
        )
        .then((r) => _resolve(r.data))
        .catchError((e) => _reject(e));
  }

  /// 文件下载, urlPath是文件路径参数, savePath是本地保存下载文件的路径
  Future download(
    String urlPath,
    savePath, {
    ProgressCallback? onReceiveProgress,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    bool deleteOnError = true,
    lengthHeader = HttpHeaders.contentLengthHeader,
    data,
    Options? options,
  }) async {
    return await _dio.download(
      urlPath,
      savePath,
      onReceiveProgress: onReceiveProgress,
      queryParameters: queryParameters,
      cancelToken: cancelToken,
      deleteOnError: deleteOnError,
      lengthHeader: lengthHeader,
      data: data,
      options: options,
    );
  }

  Future<ResponseData<dynamic>> patch<T>(
    String path, {
    data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    return await _dio
        .patch<ResponseData<T>>(
          path,
          data: data,
          queryParameters: queryParameters,
          options: options,
          cancelToken: cancelToken,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress,
        )
        .then((r) => _resolve(r.data))
        .catchError((e) => _reject(e));
  }

  /// 正常响应格式去除外包装
  Future<ResponseData<T>> _resolve<T>(response) {
    if (response is! Future) {
      response = Future.value(response);
    }
    return response.then<ResponseData<T>>((data) {
      return _assureResponseData<T>(data)!;
    }, onError: (err) {
      return _assureResponseData<T>(err);
    });
  }

  /// 异常响应格式去除外包装
  Future<ResponseData<T>> _reject<T>(err) {
    // 处理cache数据
    if (err.error != null && err.error is prefix0.HttpException) {
      var error = err.error as prefix0.HttpException;
      if (-1000 == error.code) {
        dynamic cacheData = error.response!.data;
        return _resolve(ResponseData<T>(
          code: cacheData["code"],
          msg: cacheData["msg"],
          data: cacheData["data"],
        ));
      }
    }
    if (err is! Future) {
      err = Future.error(err);
    }
    return err.then<ResponseData<T>>((v) {
      throw _assureHttpException(v)!;
    }, onError: (e) {
      throw _assureHttpException(e)!;
    });
  }

  ResponseData<T>? _assureResponseData<T>(response) {
    if (response is ResponseData<T>) {
      return response;
    }
    if (response is Response<ResponseData<T>>) {
      return response.data;
    } else if (response.data == null || response.data is! ResponseData) {
      response = ResponseData<T>(
        code: ErrorCode.SUCCESS.code,
        msg: ErrorCode.SUCCESS.msg,
        data: response.data,
      );
    } else {
      var data = response.data;
      response = ResponseData<T>(
        code: data?.code,
        msg: data?.msg,
        data: data?.data,
      );
    }
    return response;
  }

  ResponseData<T>? _assureHttpException<T>(err) {
    if (err is DioError) {
      // 如果抛出的异常就是规范的ResponseData数据,直接返回即可
      if (err.error != null && err.error is ResponseData) {
        return err.error;
      }
      // 如果抛出的异常是自定义的HttPException
      if (err.error != null && err.error is prefix0.HttpException) {
        var error = err.error as prefix0.HttpException;
        return ResponseData(
          code: error.code,
          msg: error.msg,
        );
      }
      if (err.response == null) {
        return ResponseData(
          code: ErrorCode.NETWORK_ERROR.code,
          msg: _isDebug ? err.error.toString() : err.message,
        );
      }
      return err.response?.data;
    } else {
      return ResponseData(
        code: ErrorCode.NETWORK_ERROR.code,
        msg: _isDebug ? err.toString() : ErrorCode.NETWORK_ERROR.msg,
        data: err.response?.data,
      );
    }
  }

//  /// 代理dio的resolve方法,这只是后门,非特殊情况不要调用
//  Future<Response<T>> resolve<T>(response) {
//    return _dio.resolve(response);
//  }
//
//  /// 代理dio的reject方法,这只是后门,非特殊情况不要调用
//  Future<Response<T>> reject<T>(err) {
//    return _dio.reject(err);
//  }

  /// 合并请求头contentType
  Options _mergeOption(Options? old, String contentType) {
    if (old == null) {
      return Options(
        contentType: contentType,
      );
    } else {
      old.contentType = contentType;
      return old;
    }
  }
}
